
// Copyright (C) 2012 Luca Piccioni
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//  
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//  
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;

using Derm.Raster;

namespace Derm.Xml.Collada
{
	/// <summary>
	/// The image element declares the storage for the graphical representation of an object. 
	/// The image element best describes raster image data, but can conceivably handle other 
	/// forms of imagery. The image elements allows for specifying an external image file with 
	/// the init_from element or embed image data with the data element.
	/// </summary>
	public class ColladaImage : ColladaXmlSerializable, IColladaInstantiable
	{
		#region Constructors

		/// <summary>
		/// Construct a ColladaImage.
		/// </summary>
		/// <param name="parent">
		/// The parent element containing this ColladaImage.
		/// </param>
		public ColladaImage(ColladaXmlSerializable parent) : base(parent)
		{
			
		}

		#endregion

		#region Collada Image Information

		/// <summary>
		/// The id attribute is a text string containing the unique identifier of this element. This value 
		/// must be unique within the instance document. Optional attribute.
		/// </summary>
		public string Id;

		/// <summary>
		/// The name attribute is the text string name of this element. Optional attribute.
		/// </summary>
		public string Name;

		/// <summary>
		/// The format attribute is a text string value that indicates the image format. Optional attribute.
		/// </summary>
		public string Format;

		/// <summary>
		/// The height attribute is an integer value that indicates the height of the image in pixel units. Optional attribute.
		/// </summary>
		public uint Height;

		/// <summary>
		/// The width attribute is an integer value that indicates the width of the image in pixel units. Optional attribute.
		/// </summary>
		public uint Width;

		/// <summary>
		/// The depth attribute is an integer value that indicates the depth of the image in pixel units. 
		/// A 2-D image has a depth of 1, which is also the default value. Optional attribute.
		/// </summary>
		public uint Depth = 1;

		/// <summary>
		/// The image element may contain an asset element.
		/// </summary>
		public ColladaAsset Asset;

		/// <summary>
		/// The data child element contains a sequence of hexadecimal encoded  binary octets representing the embedded image data.
		/// </summary>
		public byte[] Data;

		/// <summary>
		/// The init_from element allows you to specify an external image file to use for the image element.
		/// </summary>
		public string InitFrom;

		/// <summary>
		/// The extra element may appear any number of times.
		/// </summary>
		public readonly List<ColladaExtra> Extras = new List<ColladaExtra>();

		#endregion

		#region COLLADA Support

		internal Image Create()
		{
			ColladaDocument colladaDocument = GetColladaDocument();

			if (InitFrom == null) {
				throw new NotSupportedException();
			} else
				return (ImageCodec.Instance.Load(Path.Combine(colladaDocument.DocumentDirectory, InitFrom)));
		}

		#endregion

		#region ColladaXmlSerializable Overrides

		/// <summary>
		/// Read the attribute of the current XML serializable.
		/// </summary>
		/// <param name="xmlReader">
		/// A <see cref="XmlReader"/> used for reading XML element attributes.
		/// </param>
		/// <remarks>
		/// <para>
		/// This method is called only in the case there are effectively attributes on XML element. Don't expect that this
		/// method will be called even when the attributes count is zero. This is very important, since the design doesn't
		/// allow object initialization using this method.
		/// </para>
		/// </remarks>
		public override void ReadAttributes(XmlReader xmlReader)
		{
			if (xmlReader == null)
				throw new ArgumentNullException("xmlReader");

			string attribute;

			if ((attribute = xmlReader[ColladaAttributeId]) != null)
				Id = attribute;
			if ((attribute = xmlReader[ColladaAttributeName]) != null)
				Name = attribute;
			if ((attribute = xmlReader[ColladaAttributeFormat]) != null)
				Format = attribute;
			if ((attribute = xmlReader[ColladaAttributeHeight]) != null)
				Height = UInt32.Parse(attribute);
			if ((attribute = xmlReader[ColladaAttributeWidth]) != null)
				Width = UInt32.Parse(attribute);
			if ((attribute = xmlReader[ColladaAttributeDepth]) != null)
				Depth = UInt32.Parse(attribute);
		}

		/// <summary>
		/// Read an element of the current XML serializable.
		/// </summary>
		/// <param name="elementName">
		/// A <see cref="System.String"/> that specify the element name.
		/// </param>
		/// <param name="xmlReader">
		/// A <see cref="XmlReader"/> used for reading child element. It is positioned at the start element.
		/// </param>
		/// <remarks>
		/// <para>
		/// This method is responsible to read the start element of the nested element.
		/// </para>
		/// <para>
		/// This method is resposible to read all children elements till the corresponding end element, without reading it. In the
		/// case this condition is not satisfied, the XML parsing will be aborted.
		/// </para>
		/// </remarks>
		public override void ReadElement(string elementName, XmlReader xmlReader)
		{
			if (elementName == null)
				throw new ArgumentNullException("elementName");
			if (xmlReader == null)
				throw new ArgumentNullException("xmlReader");

			if (elementName == ColladaElementAsset) {
				Asset = new ColladaAsset(this);
				Asset.ReadXml(xmlReader);
			} else if (elementName == ColladaElementData) {
				XmlHelper.SkipXmlElement(xmlReader);
			} else if (elementName == ColladaElementInitFrom) {
				InitFrom = XmlHelper.ReadXmlTextElement(xmlReader);
			} else if (elementName == ColladaElementExtra) {
				ColladaExtra colladaExtra = new ColladaExtra(this);
				colladaExtra.ReadXml(xmlReader);
				Extras.Add(colladaExtra);
			} else
				base.ReadElement(elementName, xmlReader);
		}

		/// <summary>
		/// Collada 'image' attribute name (1.4).
		/// </summary>
		private const string ColladaAttributeId = "id";

		/// <summary>
		/// Collada 'image' attribute name (1.4).
		/// </summary>
		private const string ColladaAttributeName = "name";

		/// <summary>
		/// Collada 'image' attribute name (1.4).
		/// </summary>
		private const string ColladaAttributeFormat = "format";

		/// <summary>
		/// Collada 'image' attribute name (1.4).
		/// </summary>
		private const string ColladaAttributeHeight = "height";

		/// <summary>
		/// Collada 'image' attribute name (1.4).
		/// </summary>
		private const string ColladaAttributeWidth = "width";

		/// <summary>
		/// Collada 'image' attribute name (1.4).
		/// </summary>
		private const string ColladaAttributeDepth = "depth";

		/// <summary>
		/// Collada 'image' child element name (1.4).
		/// </summary>
		private const string ColladaElementAsset = "asset";

		/// <summary>
		/// Collada 'image' child element name (1.4).
		/// </summary>
		private const string ColladaElementData = "data";

		/// <summary>
		/// Collada 'image' child element name (1.4).
		/// </summary>
		private const string ColladaElementInitFrom = "init_from";

		/// <summary>
		/// Collada 'image' child element name (1.4).
		/// </summary>
		private const string ColladaElementExtra = "extra";

		#endregion

		#region IColladaInstantiable Implementation

		/// <summary>
		/// Instantiable identifier.
		/// </summary>
		public string InstanceId { get { return (Id); } }

		#endregion
	}
}
